package com.gaga.console.core.service.impl;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.thread.ThreadUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.gaga.common.constant.BvConstants;
import com.gaga.common.enums.*;
import com.gaga.common.exception.BusException;
import com.gaga.common.result.AudioDeviceResult;
import com.gaga.common.utils.DeviceInfoUtils;
import com.gaga.common.utils.DirectoryCleaner;
import com.gaga.common.utils.FileCrypto;
import com.gaga.common.utils.TemplateReplacer;
import com.gaga.console.base.entity.sse.PushMessageDto;
import com.gaga.console.base.mybatisplus.LambdaQueryWrapperExt;
import com.gaga.console.base.utils.MyDecimalUtil;
import com.gaga.console.base.utils.MyStringUtil;
import com.gaga.console.base.utils.SseEmitterServerUtils;
import com.gaga.console.base.utils.VideoPlayerUtils;
import com.gaga.console.core.entity.BvScriptCategory;
import com.gaga.console.core.entity.BvScriptItem;
import com.gaga.console.core.entity.SysConfig;
import com.gaga.console.core.entity.dto.*;
import com.gaga.console.core.service.*;
import com.gaga.console.core.service.impl.multimodel.ModelFactory;
import com.gaga.console.core.service.impl.multimodel.ModelHandler;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.crypto.SecretKey;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author liujia
 * @since 2025-03-27
 */
@Service
public class BvAudioPlayServiceImpl implements BvAudioPlayService {

    private static final Logger log = LoggerFactory.getLogger(BvAudioPlayServiceImpl.class);
    private static final int MODEL_START_TIMEOUT_SECONDS = 60;
    private static final int MIN_SCRIPT_QUEUE_SIZE = 5;
    // 主音频队列最大限制
    private static final int MAX_MAIN_AUDIO_QUEUE_SIZE = 5;

    // 使用 ConcurrentHashMap 作为缓存
    private final ConcurrentHashMap<String, String> userCountCache = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, StartLiveStepType> startLiveStatusCache = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, String> playLogPathCache = new ConcurrentHashMap<>();

    public static final String START_LIVE_STATUS_KEY = "startLiveStatus";
    public static final String USER_COUNT_KEY = "userCount";
    public static final String PLAY_LOG_PATH = "playLogPath";

    // 使用AtomicInteger替代ThreadLocal
    private final AtomicInteger mainAudioGenIndex = new AtomicInteger(0);
    private final AtomicInteger supassMaxCharLengthTimes = new AtomicInteger(0);

    @Autowired
    private SysConfigService sysConfigService;
    @Autowired
    private BvScriptItemService bvScriptItemService;
    @Autowired
    private BvScriptCategoryService bvScriptCategoryService;
    @Autowired
    private BvModelService bvModelService;
    @Autowired
    private BvConfigService bvConfigService;
    @Autowired
    private AudioPlayQueueService audioPlayQueueService;
    @Autowired
    private AudioGenQueueService audioGenQueueService;
    @Autowired
    private AssistantService assistantService;

    private final AtomicBoolean isStop = new AtomicBoolean(false);
    private final ExecutorService startLiveExecutor = ThreadUtil.newSingleExecutor();
    private final ExecutorService producerExecutor = ThreadUtil.newSingleExecutor();

    @Override
    public void startLive(StartLiveDto dto) throws FileNotFoundException {
        Assert.notNull(dto.getCategoryId(), "分类id不能为空");

        if (dto.getCategoryId() == null) {
            throw new BusException("请先维护话术并选择话术分类");
        }

        LambdaQueryWrapperExt<BvScriptItem> bvScriptItemLambdaQueryWrapperExt = new LambdaQueryWrapperExt<>();
        bvScriptItemLambdaQueryWrapperExt.eq(BvScriptItem::getScriptCategoryId, dto.getCategoryId());
        bvScriptItemLambdaQueryWrapperExt.eq(BvScriptItem::getDelFlag, DelFlag.OK.getCode());
        long count = bvScriptItemService.count(bvScriptItemLambdaQueryWrapperExt);

        if (count < 1) {
            throw new BusException("请先维护话术");
        }

        validateConfig(bvConfigService.getConfig());

        startLiveExecutor.submit(() -> {
            try {
                startLiveProcess(dto);
            } catch (Exception e) {
                log.error("启动直播失败", e);
                updateStartLiveStep(StartLiveStepType.startErr);
                throw new BusException("启动直播失败: " + e.getMessage());
            }
        });
    }

    private void validateConfig(ConfigDto configDto) {
        if (StringUtils.isBlank(configDto.getAiSetting().getToken())) {
            validateSmartReplySettings(configDto);
        }

        if (StringUtils.isBlank(configDto.getOutMediaDevice())) {
            throw new BusException("未设置音频输出设备，请检查音频输出设置是否正常");
        }

        List<AudioDeviceResult> audioDeviceList = DeviceInfoUtils.getAudioDeviceList();
        if (CollectionUtils.isEmpty(audioDeviceList)) {
            throw new BusException("未找到音频设备，请检查音频设备是否正常");
        }
        Optional<AudioDeviceResult> any = audioDeviceList.stream()
                .filter(audioDevice -> audioDevice.getDeviceId().equals(configDto.getOutMediaDevice())).findAny();

        if (!any.isPresent()) {
            throw new BusException("音频输出设备设置错误，请检查音频输出设置是否正常");
        }

    }

    private void validateSmartReplySettings(ConfigDto configDto) {
        if (configDto.getSmartReplySetting().getOpen()) {
            throw new BusException("您开启了智能回复，请先设置AI模型密钥");
        }
        if (configDto.getKeywordSetting().getSmartReply().getOpen()) {
            throw new BusException("您开启了关键词智能润色，请先设置AI模型密钥");
        }
        if (configDto.getUserEnterSetting().getSmartReply().getOpen()) {
            throw new BusException("您开启了用户新进智能润色，请先设置AI模型密钥");
        }
        if (configDto.getMainAudioSetting().getSmartReply().getOpen()) {
            throw new BusException("您开启了主音频智能润色，请先设置AI模型密钥");
        }
    }

    private void startLiveProcess(StartLiveDto dto) {
        try {
            updateStartLiveStep(StartLiveStepType.starting);

            // 1. 初始化模型
            initializeModels();

            // 2. 准备脚本
            List<BvScriptItem> scriptList = prepareScripts(dto);

            // 3. 设置直播状态
            setupLiveStatus();

            // 4. 创建日志目录
            String playLogPath = createPlayLogDirectory(scriptList);

            // 5. 开始生成音频
            startAudioGeneration(scriptList, playLogPath);

        } catch (Exception e) {
            log.error("启动直播过程中发生错误", e);
            updateStartLiveStep(StartLiveStepType.startErr);
            throw new BusException("启动直播失败: " + e.getMessage());
        }
    }

    @Override
    public void initializeModels() {
        // 确保所需服务都已初始化
        if (bvConfigService == null || bvModelService == null || assistantService == null) {
            log.error("必要的服务未初始化完成，无法初始化模型");
            throw new BusException("服务未完全初始化");
        }

        try {
            ConfigDto config = bvConfigService.getConfig();
            if (config == null || config.getModelSetting() == null) {
                log.error("模型配置为空，无法初始化模型");
                throw new BusException("模型配置为空");
            }

            List<String> modelList = Arrays.asList(
                    config.getModelSetting().getAnchor(),
                    config.getModelSetting().getAssistant()
            );

            // 解密模型
            decryptModel(modelList);

            // 切换模型
            bvModelService.switchModel(PlayerType.ANCHOR, config.getModelSetting().getAnchor());
            bvModelService.switchModel(PlayerType.ASSISTANT, config.getModelSetting().getAssistant());

            // 启动模型
            try {
                assistantService.startModel();
                waitForModelStart();
            } catch (Exception e) {
                log.error("模型启动失败", e);
                throw new BusException("模型启动失败: " + e.getMessage());
            }
        } catch (Exception e) {
            log.error("初始化模型过程中发生错误", e);
            throw new BusException("初始化模型失败: " + e.getMessage());
        }
    }

    private void waitForModelStart() {
        long startTime = System.currentTimeMillis();
        while (!assistantService.isModelRunning()) {
            if (System.currentTimeMillis() - startTime > MODEL_START_TIMEOUT_SECONDS * 1000) {
                throw new BusException("模型启动超时");
            }
            ThreadUtil.sleep(1000);
        }
    }

    private List<BvScriptItem> prepareScripts(StartLiveDto dto) {
        LambdaQueryWrapperExt<BvScriptItem> wraps = new LambdaQueryWrapperExt<>();
        wraps.eq(BvScriptItem::getDelFlag, DelFlag.OK.getCode())
                .eq(BvScriptItem::getScriptCategoryId, dto.getCategoryId())
                .eq(BvScriptItem::getScriptType, ScriptType.MAIN_AUDIO.getCode());

        List<BvScriptItem> scriptList = bvScriptItemService.list(wraps);
        if (CollectionUtils.isEmpty(scriptList)) {
            throw new BusException("请先维护话术脚本");
        }

        List<BvScriptItem> scriptResultList = new ArrayList<>();

        Map<String, List<BvScriptItem>> distinctNameMap = scriptList.stream().collect(Collectors.groupingBy(BvScriptItem::getItemName));
        for (String scriptName : distinctNameMap.keySet()) {
            //将住音频下的所有脚本合并成一个
            List<BvScriptItem> bvScriptItems = distinctNameMap.get(scriptName);
            if (CollectionUtils.isNotEmpty(bvScriptItems) && bvScriptItems.size() > 1) {
                List<String> collect = bvScriptItems.stream().map(BvScriptItem::getScriptContent).distinct().collect(Collectors.toList());
                BvScriptItem scriptItem = bvScriptItems.get(0);
                scriptItem.setScriptContent(String.join("\r\n", collect));
                scriptResultList.add(scriptItem);
            } else if (bvScriptItems.size() == 1) {
                scriptResultList.add(bvScriptItems.get(0));
            }
        }

        MyStringUtil.sortByName(scriptResultList);
        return scriptResultList;
    }

    private void setupLiveStatus() {
        audioPlayQueueService.stopLive();
        isStop.set(false);

        // 重置mainAudioGenIndex为0
        mainAudioGenIndex.set(0);
        supassMaxCharLengthTimes.set(0);
    }

    private String createPlayLogDirectory(List<BvScriptItem> scriptList) throws IOException {
        BvScriptCategory scriptCategory = bvScriptCategoryService.getById(scriptList.get(0).getScriptCategoryId());
        String playLogFilePath = sysConfigService.getAbsolutePathByKey(BvConstants.PLAY_LOG_FILE_PATH);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        String formattedDateTime = sdf.format(new Date());
        long timestamp = System.currentTimeMillis();

        String playLogPath = playLogFilePath + File.separator +
                scriptCategory.getScriptCategoryName() + File.separator +
                "play_log_" + formattedDateTime + "_" + timestamp + ".log";

        createLogFile(playLogPath);
        playLogPathCache.put(PLAY_LOG_PATH, playLogPath);
        return playLogPath;
    }

    private void createLogFile(String playLogPath) throws IOException {
        Path playLogFile = Paths.get(playLogPath);
        Files.createDirectories(playLogFile.getParent());
        if (!Files.exists(playLogFile)) {
            Files.createFile(playLogFile);
            log.info("直播日志文件创建成功！{}", playLogPath);
        }
    }

    private void startAudioGeneration(List<BvScriptItem> scriptList, String playLogPath) {
        updateStartLiveStep(StartLiveStepType.generating);

        if (!producerExecutor.isShutdown()) {
            producerExecutor.submit(() -> runAudioGenerationLoop(scriptList, playLogPath));
        }
    }

    private void runAudioGenerationLoop(List<BvScriptItem> scriptList, String playLogPath) {
        // 设置生成队列的目标大小
        final int TARGET_GEN_QUEUE_SIZE = 10;
        final int MIN_GEN_QUEUE_SIZE = 5;
        
        while (!isStop.get()) {
            try {
                if (LiveRoomStatusType.pause.getCode().equals(getLiveRoomStatus())) {
                    ThreadUtil.sleep(1000);
                    continue;
                }

                // 获取队列大小信息
                Integer anchorQueueSize = audioPlayQueueService.getQueueSize(PlayerType.ANCHOR);
                Integer mainAudioQueueSize = audioPlayQueueService.getQueueSizeByScriptType(ScriptType.MAIN_AUDIO);
                Integer genQueueSize = audioGenQueueService.getQueueSize();

                // 检查主音频队列是否已满
                if (mainAudioQueueSize >= MAX_MAIN_AUDIO_QUEUE_SIZE) {
                    log.info("主音频队列已达到上限 {}，暂停生成新的主音频...", MAX_MAIN_AUDIO_QUEUE_SIZE);
                    ThreadUtil.sleep(2000);
                    continue;
                }

                if (!LiveRoomStatusType.playing.getCode().equals(getLiveRoomStatus())) {
                    generateNextAudio(scriptList.get(mainAudioGenIndex.get()), playLogPath);
                    updateMainAudioIndex(scriptList.size());
                    long sleepTime = calculateGenSleepTime(genQueueSize);
                    ThreadUtil.sleep(sleepTime);
                } else {
                    // 只有当生成队列低于目标值且主音频队列未满时才生成新的主音频
                    if (genQueueSize < TARGET_GEN_QUEUE_SIZE && mainAudioQueueSize < MAX_MAIN_AUDIO_QUEUE_SIZE) {
                        generateNextAudio(scriptList.get(mainAudioGenIndex.get()), playLogPath);
                        updateMainAudioIndex(scriptList.size());
                        
                        // 根据队列大小动态调整休眠时间
                        if (genQueueSize >= MIN_GEN_QUEUE_SIZE) {
                            // 队列较满时，休眠时间更长
                            ThreadUtil.sleep(3000);
                        } else {
                            // 队列较空时，快速生成
                            ThreadUtil.sleep(1000);
                        }
                    } else {
                        // 生成队列已满或主音频队列已满，等待处理
                        String reason = genQueueSize >= TARGET_GEN_QUEUE_SIZE ? "生成队列已满" : "主音频队列已满";
                        log.info("{}({}), 等待处理中...", reason, mainAudioQueueSize);
                        ThreadUtil.sleep(2000);
                    }
                }
            } catch (Exception e) {
                log.error("音频生成过程中发生错误", e);
                // 发生错误时，适当休眠避免频繁重试导致资源消耗
                ThreadUtil.sleep(1000);
            }
        }
    }

    private void updateMainAudioIndex(int scriptListSize) {
        int currentIndex = mainAudioGenIndex.getAndIncrement();
        if (currentIndex >= scriptListSize - 1) {
            mainAudioGenIndex.set(0);
        }
    }

    @Override
    public void decryptModel(List<String> modelNameList) {
        try {
            SecretKey secretKey = FileCrypto.getSecretKey();
            String encryptModelPath = sysConfigService.getAbsolutePathByKey(BvConstants.ENCRYPT_MODEL_PATH);

            String modelPath = sysConfigService.getConfigValueByKey(BvConstants.MODEL_PATH);
            String modelWorkspacePath = sysConfigService.getAbsolutePathByKey(BvConstants.MODEL_WORKSPACE_PATH);

            FileCrypto.processDirectory(Path.of(encryptModelPath), Path.of(modelWorkspacePath + File.separator + modelPath), secretKey, false, modelNameList);
        } catch (Exception e) {
            log.error("获取密钥失败", e);
        }
    }

    private void cleanModel() {

        String modelPath = sysConfigService.getConfigValueByKey(BvConstants.MODEL_PATH);
        String modelWorkspacePath = sysConfigService.getAbsolutePathByKey(BvConstants.MODEL_WORKSPACE_PATH);
        Path path = Path.of(modelWorkspacePath + File.separator + modelPath);

        try {
            DirectoryCleaner.cleanDirectory(path);
            log.info("清理模型成功" + path);
        } catch (IOException e) {
            log.error("清理模型失败", e.getMessage());
        }
    }

    private void mainAudioTts(BvScriptItem scriptItem, String playLogPath, String categoryName) throws FileNotFoundException {
        if (scriptItem == null) {
            return;
        }
        if (StringUtils.isBlank(scriptItem.getScriptContent())) {
            return;
        }

        String[] lines = StringUtils.split(scriptItem.getScriptContent(), "\r\n");
        if (lines == null || lines.length == 0) {
            return;
        }

        boolean hasNonEmpty = Arrays.stream(lines).anyMatch(line -> line != null && !line.trim().isEmpty());
        if (!hasNonEmpty) {
            log.error("列表中没有非空元素");
            return;
        }

        int randomIndex = ThreadLocalRandom.current().nextInt(lines.length);
        String generateAudioPathConfig = sysConfigService.getAbsolutePathByKey(BvConstants.GENERATE_AUDIO_PATH);

        ConfigDto configDto = bvConfigService.getConfig();
        String generateAudioPath = generateAudioPathConfig;
        //生成音频文件路径
        if (!generateAudioPath.endsWith(File.separator)) {
            generateAudioPath = generateAudioPath + File.separator;
        }
        generateAudioPath = generateAudioPath + categoryName + File.separator + ScriptType.MAIN_AUDIO.getCode() + File.separator;

        String text = lines[randomIndex];
        Integer maxCharLength = configDto.getAudioSetting().getAudioGen().getMaxCharLength();

        //随机取话术中的文本后，判断长度
        String randomSelectText = TemplateReplacer.randomSelectText(text);
        if (randomSelectText.length() > maxCharLength) {
            supassMaxCharLengthTimes.incrementAndGet();
            return;
        }

        AudioGenDto audioGenDto = new AudioGenDto();
        audioGenDto.setText(text)
                .setConfigDto(configDto)
                .setScriptItem(scriptItem)
                .setGenerateAudioPath(generateAudioPath)
                .setSource("mainAudio")
                .setPlayLogPath(playLogPath);
        log.error("生成一个主音频加入到生成队列，加入前队列大小：{}", audioGenQueueService.getQueueSize());
        audioGenQueueService.addToQueue(audioGenDto);
    }

    @Override
    public void stopLive() {
        isStop.set(true);
        audioPlayQueueService.stopLive();
        audioGenQueueService.stopGen();
        VideoPlayerUtils.closeWindow();

        // 重置mainAudioGenIndex为0
        mainAudioGenIndex.set(0);
        supassMaxCharLengthTimes.set(0);

        cleanupLiveStatus();
        cleanModel();

        log.info("停止直播");
    }

    private void cleanupLiveStatus() {
        ConfigDto config = bvConfigService.getConfig();
        List<PushMessageType> needCleanTypeList = PushMessageType.getNeedCleanTypeList();
        for (PushMessageType pushMessageType : needCleanTypeList) {
            PushMessageDto pushMessageDto = new PushMessageDto();
            pushMessageDto.setMessage("");
            pushMessageDto.setType(pushMessageType);
            SseEmitterServerUtils.sendMessage(config.getSseChannelId(), pushMessageDto);
        }
        setLiveStatus(LiveRoomStatusType.stop);
    }

    @Override
    public String getLiveRoomStatus() {
        return sysConfigService.getConfigValueByKey(BvConstants.LIVE_ROOM_STATUS);
    }

    @Override
    public void setLiveStatus(LiveRoomStatusType liveRoomStatusType) {
        if (LiveRoomStatusType.pause.equals(liveRoomStatusType)) {
            audioPlayQueueService.pause();
        } else if (LiveRoomStatusType.playing.equals(liveRoomStatusType)) {
            String configValueByKey = sysConfigService.getConfigValueByKey(BvConstants.LIVE_ROOM_STATUS);
            if (LiveRoomStatusType.pause.getCode().equals(configValueByKey)) {
                //恢复播放
                audioPlayQueueService.play();
            }
        }
        UpdateWrapper<SysConfig> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq(BvConstants.CONFIG_KEY, BvConstants.LIVE_ROOM_STATUS)
                .set(BvConstants.CONFIG_VALUE, liveRoomStatusType.getCode());
        sysConfigService.update(updateWrapper);
    }

    @Override
    public void generate(PlayDto playDto) {

        //直播间启动状态
        String liveRoomStatus = getLiveRoomStatus();
        if (StringUtils.isBlank(liveRoomStatus) || !liveRoomStatus.equals(LiveRoomStatusType.playing.getCode())) {
            return;
        }
        ConfigDto configDto = bvConfigService.getConfig();

        ConfigDto.BlackListSetting blackListSetting = configDto.getBlackListSetting();
        if (StringUtils.isNotBlank(blackListSetting.getBlackList()) && StringUtils.isNotBlank(playDto.getUserName())) {
            String[] split = blackListSetting.getBlackList().split("\\R");
            Optional<String> any = Arrays.stream(split).filter(blackItem -> StringUtils.isNotBlank(blackItem) && playDto.getUserName().contains(blackItem.trim())).findAny();
            if (any.isPresent()) {
                return;
            }
        }

        Integer maxCharLength = configDto.getAudioSetting().getAudioGen().getMaxCharLength();

        if (StringUtils.isNotBlank(playDto.getText()) && playDto.getText().length() > maxCharLength) {
            return;
        }

        // 判断是否打开关键字回复或者用户进入
        Boolean open = false;
        switch (playDto.getScriptType()) {
            case KEYWORD:
                open = configDto.getKeywordSetting().getOpen();
                break;
            case USER_ENTER:
                open = configDto.getUserEnterSetting().getOpen();
                break;
            default:
                open = true;
                break;
        }
        if (!open) {
            return;
        }

        if (ScriptType.RANDOM_AUDIO.equals(playDto.getScriptType())) {
            QueryWrapper<BvScriptItem> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("category_id", configDto.getFileDirectory().getCategoryId());
            queryWrapper.eq("script_type", playDto.getScriptType().getCode());
            List<BvScriptItem> list = bvScriptItemService.list(queryWrapper);
            if (CollectionUtils.isEmpty(list)) {
                return;
            }
            int index = ThreadLocalRandom.current().nextInt(list.size());
            BvScriptItem bvScriptItem = list.get(index);
            if (StringUtils.isBlank(bvScriptItem.getScriptContent())) {
                return;
            }
            //是音频文件直接播放
            if (isStop.get()) {
                return;
            }
            if (bvScriptItem.getScriptContent().endsWith(BvConstants.WAV_FORMAT)) {

                AudioPlayDto audioDto = new AudioPlayDto().setAudioFilePath(bvScriptItem.getScriptContent())
                        .setScriptType(playDto.getScriptType())
                        .setPlayLogPath(playLogPathCache.get(PLAY_LOG_PATH))
                        .setVolume(MyDecimalUtil.getFloatVolume(configDto.getAudioSetting().getVolume().getLocal()));
                //正在播放的音频播放完成后插入到第一条
                audioPlayQueueService.insertFirstToQueue(audioDto, PlayerType.ANCHOR);
                return;
            } else {
                playDto.setText(bvScriptItem.getScriptContent());
            }
        }

        try {
            String speaker = getSpeaker(playDto, configDto);
            String modelName = getAudioModelName(speaker, configDto);

            PlayerType playerType = PlayerType.getPlayerTypeByCode(speaker);
            ModelHandler handler = ModelFactory.getHandler(playerType, sysConfigService, bvConfigService, modelName, null, null);

            String currentModelName = handler.getCurrentModel();
            String generateAudioPath = sysConfigService.getAbsolutePathByKey(BvConstants.GENERATE_AUDIO_PATH);
            if (StringUtils.isBlank(currentModelName)) {
                throw new BusException("请先选择模型");
            }

            //生成音频文件路径
            if (!generateAudioPath.endsWith(File.separator)) {
                generateAudioPath = generateAudioPath + File.separator;
            }
            generateAudioPath = generateAudioPath + modelName + File.separator + playDto.getScriptType().getCode() + File.separator + System.currentTimeMillis() + BvConstants.WAV_FORMAT;
            AudioGenDto audioGenDto = new AudioGenDto();
            audioGenDto.setConfigDto(configDto).
                    setText(playDto.getText())
                    .setGenerateAudioPath(generateAudioPath)
                    .setSource("generate")
                    .setPlayDto(playDto)
                    .setSpeaker(speaker)
                    .setPlayLogPath(playLogPathCache.get(PLAY_LOG_PATH));

            log.error("生成一个generate加入到生成队列，加入前队列大小：{}", audioGenQueueService.getQueueSize());
            audioGenQueueService.insertFirstToQueue(audioGenDto);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    @Override
    public String getUserCount() {
        return userCountCache.get(USER_COUNT_KEY);
    }

    @Override
    public void updateUserCount(UpdateRealTimeDataDto dto) {
        if (StringUtils.isBlank(dto.getUserCount())) {
            return;
        }
        userCountCache.put(USER_COUNT_KEY, dto.getUserCount());
    }

    @Override
    public String getStartLiveStep() {
        return startLiveStatusCache.get(START_LIVE_STATUS_KEY).getDesc();
    }

    @Override
    public void updateStartLiveStep(StartLiveStepType startLiveStatusType) {
        if (startLiveStatusType == null) {
            return;
        }
        ConfigDto config = bvConfigService.getConfig();
        PushMessageDto pushMessageDto = new PushMessageDto();
        pushMessageDto.setType(PushMessageType.startLiveStep);
        pushMessageDto.setMessage(startLiveStatusType.getDesc());
        pushMessageDto.setStartLiveStatusType(startLiveStatusType.getCode());

        SseEmitterServerUtils.sendMessage(config.getSseChannelId(), pushMessageDto);
    }

    /**
     * 获取生成音频的模型名称
     *
     * @param speaker
     * @param configDto
     * @return
     */
    private static String getAudioModelName(String speaker, ConfigDto configDto) {
        ConfigDto.ModelSetting modelSetting = configDto.getModelSetting();

        String modelName = "";
        if (BvConstants.DEFAULT_SPEAKER.equals(speaker)) {
            modelName = modelSetting.getAnchor();
        } else if (BvConstants.ASSISTANT_SPEAKER.equals(speaker)) {
            modelName = modelSetting.getAssistant();
        }
        return modelName;
    }

    /**
     * 获取音频的播放器发音人
     *
     * @param dto
     * @param configDto
     * @return
     */
    private static String getSpeaker(PlayDto dto, ConfigDto configDto) {

        String speaker = BvConstants.DEFAULT_SPEAKER;
        switch (dto.getScriptType()) {
            case INSERT_AUDIO:
                speaker = configDto.getInsertAudioSetting().getType();
                break;
            case KEYWORD:
                speaker = configDto.getKeywordSetting().getType();
                break;
            case USER_ENTER:
                speaker = configDto.getUserEnterSetting().getType();
                break;
            default:
                break;
        }
        return speaker;
    }

    /**
     * 获取文本转语音入参
     *
     * @param text
     * @param modelSetting
     * @return
     */
    public static TextToSpeechFrontDto getTextToSpeechFrontDto(String text, ConfigDto.ModelSetting modelSetting) {

        TextToSpeechFrontDto textToSpeechFrontDto = new TextToSpeechFrontDto();
        textToSpeechFrontDto.setText(text);
        return textToSpeechFrontDto;
    }

    public static void main(String[] args) {
        AtomicInteger i = new AtomicInteger(0);
        System.out.println(i.get());
        i.incrementAndGet();
        System.out.println(i.get());
    }

    private long calculateGenSleepTime(Integer anchorQueueSize) {
        if (anchorQueueSize == null) {
            return 1000;
        }
        // 根据队列大小动态调整休眠时间
        if (anchorQueueSize >= 20) {
            return 20000;
        } else if (anchorQueueSize >= 15) {
            return 15000;
        } else if (anchorQueueSize >= 10) {
            return 10000;
        } else if (anchorQueueSize >= 5) {
            return 3000;
        } else {
            return 1000;
        }
    }

    private void generateNextAudio(BvScriptItem scriptItem, String playLogPath) {
        if (scriptItem == null) {
            return;
        }
        try {
            BvScriptCategory scriptCategory = bvScriptCategoryService.getById(scriptItem.getScriptCategoryId());
            if (scriptCategory == null) {
                return;
            }
            mainAudioTts(scriptItem, playLogPath, scriptCategory.getScriptCategoryName());
        } catch (Exception e) {
            log.error("生成下一个音频失败", e);
        }
    }

    @Override
    public void randomSleep() {
        Integer anchorQueueSize = audioPlayQueueService.getQueueSize(PlayerType.ANCHOR);

        long sleepTime = calculateGenSleepTime(anchorQueueSize);
        ThreadUtil.sleep(sleepTime);
    }

    @Override
    public void fixQueneSizeSleep() {
        // 向后兼容的默认实现，相当于主音频等待模式
        fixQueneSizeSleep("mainAudio");
    }

    @Override
    public void fixQueneSizeSleep(String audioSource) {
        // 获取当前主音频队列大小
        Integer anchorQueueSize = audioPlayQueueService.getQueueSize(PlayerType.ANCHOR);
        Integer mainAudioQueueSize = audioPlayQueueService.getQueueSizeByScriptType(ScriptType.MAIN_AUDIO);
        
        // 如果是主音频且队列中主音频数量大于等于阈值，则等待
        if ("mainAudio".equals(audioSource)) {
            log.info("当前主音频队列大小: {}，总队列大小: {}", mainAudioQueueSize, anchorQueueSize);
            
            // 一直等待，直到主音频队列数量小于阈值或者停止信号
            while (mainAudioQueueSize >= MAX_MAIN_AUDIO_QUEUE_SIZE && !isStop.get()) {
                log.info("主音频队列已达到上限 {}，等待中...", MAX_MAIN_AUDIO_QUEUE_SIZE);
                ThreadUtil.sleep(1000);
                // 重新获取最新的队列大小
                mainAudioQueueSize = audioPlayQueueService.getQueueSizeByScriptType(ScriptType.MAIN_AUDIO);
            }
        } else {
            // 非主音频，只记录日志但不阻塞
            if (mainAudioQueueSize >= MAX_MAIN_AUDIO_QUEUE_SIZE) {
                log.info("非主音频任务：当前主音频队列大小为{}，继续执行", mainAudioQueueSize);
            }
        }
    }

}
